//////////////////////////////////////////////////////////////////////
// BillboardParticle.h
//
// SHEN Fangyang
// me@shenfy.com
//
// Copyright (C) SHEN Fangyang, 2011, All rights reserved.
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
//  Brief Description
//
//	CPU managed particle system
//
//////////////////////////////////////////////////////////////////////
#pragma once
#include <ParticleSystem.h>

namespace grflib
{
	namespace engine
	{
		namespace aux
		{
            namespace particles
            {
                //////////////////////////////////////////
                //simple particle type
                struct CBillboardParticle
                {
                    //vertex data
                    struct vertex_type
                    {
                        D3DXVECTOR3 pos;
                        D3DXVECTOR3 velocity;
                        float angle;
                        float life;
                        float size;
                    };

                    //vertex layout
                    static const std::vector<D3D10_INPUT_ELEMENT_DESC> &GetVertexLayout(void)
                    {
                        static const D3D10_INPUT_ELEMENT_DESC desc[] = 
                        {{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
                        { "VELOCITY", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 },
                        { "ANGLE", 0, DXGI_FORMAT_R32_FLOAT, 0, 24, D3D10_INPUT_PER_VERTEX_DATA, 0 },
                        { "LIFE",     0, DXGI_FORMAT_R32_FLOAT, 0, 28, D3D10_INPUT_PER_VERTEX_DATA, 0 },
                        { "SIZE",     0, DXGI_FORMAT_R32_FLOAT, 0, 32, D3D10_INPUT_PER_VERTEX_DATA, 0 }};
                        static std::vector<D3D10_INPUT_ELEMENT_DESC> layout(&desc[0], &desc[4] + 1);

                        return layout;
                    }

                    //data member
                    vertex_type vertex;
                    float angularSpeed;
                    double birthTime;

                    //update functions
                    boost::function<void (double, float, CBillboardParticle *)> updateFunc;
                    boost::function<void (CParticleSystem<CBillboardParticle>::particle_list_type &, double, float, CBillboardParticle *)> deathFunc;
                };
                //////////////////////////////////////////


                //////////////////////////////////////////
                //simple particle update and death func
                template<typename TP>
                void RigidBodyMotionFunc(double time, float elapsedTime, TP *pParticle)
                {
                    float deltaTime = elapsedTime;
                    pParticle->vertex.life += deltaTime / 10.0f; //life time = 5 sec
                    pParticle->vertex.pos = pParticle->vertex.pos + pParticle->vertex.velocity * deltaTime;
                    pParticle->vertex.angle += pParticle->angularSpeed * deltaTime;
                    if (pParticle->vertex.angle > (2.0f * (float)D3DX_PI))
                        pParticle->vertex.angle -= (2.0f * (float)D3DX_PI);
                    else if (pParticle->vertex.angle < (-2.0f * (float)D3DX_PI))
                        pParticle->vertex.angle += (2.0f * (float)D3DX_PI);
                }

                template<typename TP>
                void RebirthFunc(typename CParticleSystem<TP>::particle_list_type &particleList,
                    double time, float elapsedTime, TP *pParticle)
                {
                    TP particle;
                    particle.vertex.pos = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
                    particle.vertex.velocity = pParticle->vertex.velocity;
                    particle.vertex.angle = pParticle->vertex.angle;
                    particle.vertex.life = -1.0f;
                    particle.vertex.size = pParticle->vertex.size;
                    particle.birthTime = time - 0.01; //delay 1 sec before rebirth
                    particle.angularSpeed = pParticle->angularSpeed;
                    particle.updateFunc = pParticle->updateFunc;
                    particle.deathFunc = pParticle->deathFunc;
                    particleList.push_back(particle);
                }

                template<typename TP>
                void SplitFunc(typename CParticleSystem<TP>::particle_list_type &particleList,
                    double time, float elapsedTime, TP *pParticle)
                {
                    TP particle;
                    particle.vertex.pos = pParticle->vertex.pos;
                    particle.vertex.velocity.x = float(rand() % 20) / 10.0f - 1.0f;
                    particle.vertex.velocity.y = float(rand() % 20) / 10.0f - 1.0f;
                    particle.vertex.velocity.z = float(rand() % 20) / 10.0f - 1.0f;
                    particle.angularSpeed = float(rand() % 20) / 10.0f - 1.0f;
                    particle.vertex.angle = pParticle->vertex.angle;
                    particle.vertex.life = -1.0f;
                    particle.vertex.size = pParticle->vertex.size;
                    particle.birthTime = time - 0.01; //delay 1 sec before rebirth
                    particle.updateFunc = pParticle->updateFunc;
                    particle.deathFunc = boost::bind(SplitFunc<TP>, _1, _2, _3, _4);
                    particleList.push_back(particle);
                    
                    particle.vertex.velocity.x = pParticle->vertex.velocity.x - particle.vertex.velocity.x;
                    particle.vertex.velocity.y = pParticle->vertex.velocity.y - particle.vertex.velocity.y;
                    particle.vertex.velocity.z = pParticle->vertex.velocity.z - particle.vertex.velocity.z;
                    particle.angularSpeed = pParticle->angularSpeed - particle.angularSpeed;

                    particle.deathFunc = boost::bind(VanishFunc<TP>, _1, _2, _3, _4);
                    particleList.push_back(particle);
                }

                template<typename TP>
                void VanishFunc(typename CParticleSystem<TP>::particle_list_type &particleList,
                    double time, float elapsedTime, TP *pParticle)
                {
                }
                //////////////////////////////////////////

            } //namespace particles
		} //namespace aux
	} //namespace engine
} //namespace grflib
